package org.freedoit.server.service.auth;

import java.io.IOException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.freedoit.common.data.FreeDoitAuthResponse;
import org.freedoit.common.util.FreeDoitDataUtils;
import org.freedoit.server.dao.DAOFactory;
import org.freedoit.server.om.User;
import org.freedoit.shared.util.Constants;

public class AuthServlet extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1906298542516433980L;
	
	public void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		// Client Id must be given, -1 means new client
		String strClientId = request.getParameter("clientId");
		if (strClientId == null) {
			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_INVALID_REQUEST), 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
			
			return;
		}
		
		// Check if ClientId is valid or not
		boolean blnNewClient = false;
		Long lClientId = new Long(-1);
		if ("-1".equals(strClientId)) {
			blnNewClient = true;
		} else {
			try {
				lClientId = Long.valueOf(strClientId);
			} catch (Exception ex) {
				response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_INVALID_REQUEST), 
						new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
				
				return;
			}
		}

		// Check User Name
		String user = request.getParameter("user");
		if (user == null || "".equals(user)) {
			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_INVALID_REQUEST), 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
			
			return;
		}

		// Check Key
		String key = request.getParameter("key");
		if (key == null || "".equals(key)) {
			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_INVALID_REQUEST), 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
			
			return;
		}
		
		User userExisted = DAOFactory.getUserDAO().findByUserName(user);
		if (userExisted == null) {
			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_USER_NOT_EXIST), 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
			
			return;
		}
		
//		if (key.equals(userExisted.getUserKey())) {
		String userKey = userExisted.getUserKey();
		if (FreeDoitDataUtils.validateEncryptedPassword(key, userKey)) {
			// 
			if (blnNewClient) {
				userExisted = DAOFactory.getUserDAO().newClientId(userExisted.getUserId());
				lClientId = userExisted.getLatestClientId();
			} else {
				// the latest client id is the max one, so if your client id is larger than it
				// this should be a not valid one
				if (lClientId > userExisted.getLatestClientId()) {
					response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_INVALID_REQUEST), 
							new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
					
					return;
				}
			}
			
			// generate random key
			KeyPair keyPair = FreeDoitDataUtils.generateKeyPair();
			if (keyPair == null) {
				response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_SERVER_ERROR), 
						new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
				
				return;
			}

			// public key information (encrypted by user's random key)
			RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
			String strModulus = FreeDoitDataUtils.encryptByAES(
					publicKey.getModulus().toString(), userKey);

			if (strModulus == null) {
				response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_SERVER_ERROR), 
						new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
				
				return;
			}
			
			String strExponent = FreeDoitDataUtils.encryptByAES(
					publicKey.getPublicExponent().toString(), userKey);

			if (strExponent == null) {
				response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_SERVER_ERROR), 
						new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
				
				return;
			}
			
			// save user and client id into http session
			request.getSession().setAttribute(Constants.DOIT_PRI_KEY, (RSAPrivateKey)keyPair.getPrivate());
			request.getSession().setAttribute(Constants.DOIT_USER_KEY, userExisted);
			request.getSession().setAttribute(Constants.DOIT_CLIENT_KEY, lClientId);

			// feed back
			FreeDoitAuthResponse arr = new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_OK);
			arr.setUserId(userExisted.getUserId());
			arr.setClientId(lClientId);
			
			arr.setKeyModulus(strModulus);
			arr.setKeyExponent(strExponent);

			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(arr, 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
		} else {
			response.getWriter().write(FreeDoitDataUtils.createStandardJson().toJson(new FreeDoitAuthResponse(FreeDoitAuthResponse.CODE_AUTH_FAILURE), 
					new com.google.gson.reflect.TypeToken<FreeDoitAuthResponse>() {}.getType()));
		}
	}

}
